#include "DLM_Manager.h"

/*============================================================================
  *** DLM_Manger ***
  =========================================================================== */

DLM_Manager::DLM_Manager()
{
	resources.clear();
}

bool DLM_Manager::lock(pid_t pid, int resource_id, int lock_type, bool non_block)
{
	int index = this->find_resource_position(resource_id);
	
	//no holded resource of this number - create a new one
	if (index == -1) {	
		DLM_Resource new_resource = DLM_Resource(resource_id);
		new_resource.add_lock(pid, lock_type);
		resources.push_back(new_resource);
		return true;
	} 
	//resource already holded 
	else {
		//next lock can be added
		if (resources[index].is_lock_possible(lock_type)) {
			resources[index].add_lock(pid, lock_type);
			return true;
		} 
		//impossible to lock resource - add to the queue
		else {
			if (non_block != true) {
				resources[index].add_queue(pid, lock_type);
			}
			return false;
		}
	}
}

vector<pid_t> DLM_Manager::unlock(pid_t pid, int resource_id)
{
	//make resource free
	int index = this->find_resource_position(resource_id);
	resources[index].free_resource(pid);
	
	//add as many new locks from waiting locks as it is possible
	vector<pid_t> new_pids = vector<pid_t>();
	while(1) {
		pid_t new_pid = resources[index].lock_resource();
		if (new_pid == -1) {
			return new_pids;
		}
		else {
			new_pids.push_back(new_pid);
		}
	}
}

void DLM_Manager::timeout(pid_t pid, int resource_id)
{
	int index = this->find_resource_position(resource_id);
	resources[index].remove_waiting_pid(pid);
}

bool DLM_Manager::try_lock(int resource_id, int lock_type)
{
	int index = this->find_resource_position(resource_id);
	
	//no holded resource of this number - create a new one
	if (index == -1) {		
		return true;
	} 
	//resource already holded 
	else {
		//next lock can be added
		if (resources[index].is_lock_possible(lock_type)) {
			return true;
		} 
		//impossible to lock resource
		else {
			return false;
		}
	}
}

int DLM_Manager::find_resource_position(int resource_id)
{
	for(unsigned int i = 0; i < resources.size(); i++) {
		if (resources[i].get_id() == resource_id) {
			return i;
		}
	} 
	return -1;
}

/*============================================================================
  *** DLM_Lock_Request ***
  =========================================================================== */

DLM_Lock_Request::DLM_Lock_Request()
{
}

DLM_Lock_Request::DLM_Lock_Request(pid_t pid, int lock_type)
{
	this->pid = pid;
	this->lock_type = lock_type;
}

pid_t DLM_Lock_Request::get_pid() const
{
	return pid;
}

int DLM_Lock_Request::get_lock_type() const
{
	return lock_type;
}

/*============================================================================
  *** DLM_Resource ***
  =========================================================================== */

DLM_Resource::DLM_Resource()
{
	current_locks.clear();
	waiting_locks.clear();
}

DLM_Resource::DLM_Resource(int resource_id)
{
	this->resource_id = resource_id;
	this->current_lock_type = -1;
	current_locks.clear();
	waiting_locks.clear();
}

int DLM_Resource::get_id() const
{	
	return resource_id;
}

bool DLM_Resource::is_lock_possible(int new_lock_type) const
{
	if (current_lock_type == -1) {
		return true;
	} else {
		return PRIVILEGES[current_lock_type][new_lock_type];
	}
}

void DLM_Resource::add_lock(pid_t pid, int lock_type)
{
	DLM_Lock_Request request = DLM_Lock_Request(pid, lock_type);
	if (current_lock_type < lock_type) {
		current_lock_type = lock_type;
	}
	current_locks.push_back(request);
}

void DLM_Resource::add_queue(pid_t pid, int lock_type)
{
	DLM_Lock_Request request = DLM_Lock_Request(pid, lock_type);
	waiting_locks.push_back(request);
}

void DLM_Resource::remove_waiting_pid(pid_t pid)
{
	vector<DLM_Lock_Request>::iterator it;
	for(it = waiting_locks.begin(); it != waiting_locks.end(); it++) {
		if (it->get_pid() == pid) {
			waiting_locks.erase(it);
			return;
		}
	}
}

void DLM_Resource::free_resource(pid_t pid) 
{
	//remove lock from resource
	vector<DLM_Lock_Request>::iterator it;
	for(it = current_locks.begin(); it != current_locks.end(); it++) {
		if (it->get_pid() == pid) {
			current_locks.erase(it);
			break;
		}
	}
	//set new current_lock_type
	current_lock_type = -1;
	if (current_locks.size() != 0) {
		for(it = current_locks.begin(); it != current_locks.end(); it++) {
			if (current_lock_type <= it->get_lock_type() ) {
				current_lock_type = it->get_lock_type();
			}
		}
	}
}

int DLM_Resource::lock_resource()
{
	if (waiting_locks.size() == 0) {
		return -1;
	}
	
	//get first waiting lock and try to move it to current locks
	DLM_Lock_Request request = waiting_locks.front();
	if (this->is_lock_possible(request.get_lock_type())) {
		this->add_lock(request.get_pid(), request.get_lock_type());
		waiting_locks.erase(waiting_locks.begin());
		return request.get_pid();
	}
	else {
		return -1;
	}
}
